package com.gitee.qdbp.jdbc.tags;

import java.io.IOException;
import com.gitee.qdbp.jdbc.api.SqlBoot;
import com.gitee.qdbp.staticize.common.NodeMetaData;
import com.gitee.qdbp.staticize.exception.TagException;
import com.gitee.qdbp.staticize.tags.base.BaseTag;
import com.gitee.qdbp.staticize.tags.base.NextStep;
import com.gitee.qdbp.staticize.utils.TagUtils;
import com.gitee.qdbp.tools.parse.StringAccess;
import com.gitee.qdbp.tools.utils.StringTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 输出表的列名<br>
 * 完整写法 &lt;columns class="@SysUserEntity" alias="u" excludes="password,userRemark" /&gt;<br>
 * 简略写法 &lt;columns value="@SysUserEntity u -password,userRemark"/&gt;
 *
 * @author zhaohuihua
 * @version 20210504
 * @since 3.4.0
 */
public class ColumnsTag extends BaseTag {

    /** 简略写法: value="@SysUserEntity u" **/
    private String value;
    /** 目标类名 **/
    private String className;
    /** 目标类 **/
    private Class<?> classType;
    /** 表别名 (可为空) **/
    private String alias;
    /** 排除字段 **/
    private String excludes;

    /** 简略写法: value="@SysUserEntity u -password,userRemark" **/
    public void setValue(String value) {
        this.value = value;
    }

    /** 简略写法: value="@SysUserEntity u -password,userRemark" **/
    public String getValue() {
        return value;
    }

    /** 目标类名 **/
    public void setClass(String className) {
        this.className = className;
    }

    /** 目标类名 **/
    public String getClassName() {
        return className;
    }

    /** 目标类名 **/
    public void setClassName(String clazz) {
        this.className = clazz;
    }

    /** 表别名 (可为空) **/
    public void setAlias(String alias) {
        this.alias = alias;
    }

    /** 排除字段 **/
    public void setExcludes(String excludes) {
        this.excludes = excludes;
    }

    /** 排除字段 **/
    public String getExcludes() {
        return excludes;
    }

    @Override
    public NextStep doHandle() throws TagException, IOException {
        SqlBoot sqlBoot = this.getStackValue("db.sqlBoot", SqlBoot.class);
        this.print(sqlBoot.sqlTools().buildSelectFieldsSql(classType, alias, excludes));
        return NextStep.SKIP_BODY;
    }

    protected Class<?> parseClass(String clazz, String fromField) {
        String className = clazz.trim();
        if (className.charAt(0) == '@') {
            if (!this.containsImportClass(className)) {
                String fmt = "Attribute '%s' error, import class not found for name '%s'";
                throw new TagException(String.format(fmt, fromField, className));
            } else {
                className = this.getImportClass(className);
            }
        }
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            String fmt = "Attribute '%s' error, class not found for name '%s'";
            throw new TagException(String.format(fmt, fromField, className));
        }
    }

    protected void parseValue(String value) {
        StringAccess sa = new StringAccess(value);
        String className = sa.readUniterm(' ', '\t', '\r', '\n');
        if (className != null) {
            this.className = className;
        } else {
            String msg = "Attribute 'value' error, the expected format is \"ClassName alias - excludeFields\", "
                    + "e.g. value=\"@SysUserEntity t -password,userRemark\"";
            throw new TagException(msg);
        }
        if (!sa.skipWhitespace().isReadable()) {
            return;
        }
        String alias = sa.readUniterm(' ', '\t', '\r', '\n');
        if (alias != null) {
            if (StringTools.isWordString(alias)) {
                this.alias = alias;
            } else {
                String fmt = "Attribute 'value' error, the alias [%s] after class must be a word.";
                throw new TagException(String.format(fmt, alias));
            }
        }
        if (!sa.skipWhitespace().isReadable()) {
            return;
        }
        if (sa.peekChar() != '-') {
            String msg = "Attribute 'value' error, the exclusion fields must be starts with '-' sign.";
            throw new TagException(msg);
        }
        // 读取排除字段
        String excludes = sa.skipChar().skipWhitespace().readToEnd();
        if (excludes != null) {
            this.excludes = excludes.trim();
        }
    }

    @Override
    public void doValidate(NodeMetaData metadata) throws TagException {
        if (VerifyTools.isBlank(value)) {
            if (VerifyTools.isBlank(className)) {
                throw new TagException("Attribute 'class' and 'value' cannot all be empty.");
            } else {
                // 解析目标类型
                this.classType = parseClass(className, "class");
            }
        } else {
            if (VerifyTools.isNotBlank(className) || VerifyTools.isNotBlank(alias)
                    || VerifyTools.isNotBlank(excludes)) {
                throw new TagException("Attribute 'value' and 'class,alias,excludes' cannot be set at the same time.");
            }
            // 解析value字符串
            this.parseValue(this.value);
            // 解析目标类型
            this.classType = parseClass(className, "value");
        }
        for (NodeMetaData item : metadata.getChildren()) {
            if (!TagUtils.isTextTag(item.getTagClass())) {
                throw new TagException("Content cannot exist in the tag body.");
            }
            String text = item.getAttributeValue("value", String.class);
            if (text != null && !StringTools.isAsciiWhitespace(text)) {
                throw new TagException("Content cannot exist in the tag body.");
            }
        }
    }
}
